package com.dly.hope.service.impl;

import com.dly.hope.constant.ExceptionConstant;
import com.dly.hope.dto.UserSocketDto;
import com.dly.hope.model.ro.AddGroupUserRo;
import com.dly.hope.model.ro.CreateGroupRo;
import com.dly.hope.model.ro.KickOutRo;
import com.dly.hope.model.ro.UpdateGroupRo;
import com.dly.hope.model.vo.GroupAddMessageVo;
import com.dly.hope.model.vo.GroupVo;
import com.dly.hope.model.vo.MemberVo;
import com.dly.hope.modle.Group;
import com.dly.hope.modle.GroupUser;
import com.dly.hope.modle.UserAccount;
import com.dly.hope.modle.WebSocketMessage;
import com.dly.hope.repository.GroupRepository;
import com.dly.hope.repository.GroupUserRepository;
import com.dly.hope.repository.SessionRepository;
import com.dly.hope.repository.UserAccountRepository;
import com.dly.hope.service.GroupService;
import com.dly.hope.type.GroupType;
import com.dly.hope.type.MessageType;
import com.dly.hope.utils.ExceptionPropertiesUtil;
import com.dly.hope.utils.JsonFormatUtil;
import com.dly.hope.webSocket.WebSocketDto;
import com.dly.hope.webSocket.WebSocketServer;
import com.dly.hope.webSocket.dto.MessageDto;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class GroupServiceImpl implements GroupService {


    @Resource
    private GroupRepository groupRepository;

    @Resource
    private GroupUserRepository groupUserRepository;

    @Resource
    private UserAccountRepository userAccountRepository;

    @Resource
    private SessionRepository sessionRepository;

    @Override
    public List<GroupVo> getGroup(String userId) {
        List<String> groupIds = groupUserRepository.findAllByMemberAndDataStatusAndIsAgree(userId, 1,1).stream().map(GroupUser::getGroupId).collect(Collectors.toList());
        return groupRepository
                .findAllByIdInAndDataStatus(groupIds,1)
                .stream()
                .map(GroupVo::toVo)
                .collect(Collectors.toList());
    }

    @Override
    public List<GroupAddMessageVo> getFriendAddMessage(String userId) {
        List<GroupAddMessageVo> result = new ArrayList<>();
        List<Group> groups = groupRepository.findAllByGroupMasterAndDataStatus(userId, 1);
        //自己的群聊
        List<String> groupIds = groups.stream().map(Group::getId).collect(Collectors.toList());
        Map<String, Group> groupMap = groups.stream().collect(Collectors.toMap(Group::getId, group -> group, (old, now) -> now));

        List<GroupUser> applicationGroupUsers = groupUserRepository.findAllByGroupIdInAndDataStatusAndIsAgreeAndGroupType(groupIds, 1, 0, GroupType.APPLICATION);
        List<String> applicationGroupUserIds = applicationGroupUsers.stream().map(GroupUser::getMember).collect(Collectors.toList());
        Map<String, UserAccount> userAccountMap = userAccountRepository.findAllByIdInAndDataStatus(applicationGroupUserIds, 1).stream().collect(Collectors.toMap(UserAccount::getId, userAccount -> userAccount, (old, now) -> now));

        //自己群的申请
        applicationGroupUsers.forEach(groupUser -> {
            UserAccount userAccount = userAccountMap.get(groupUser.getMember());
            Group group = groupMap.get(groupUser.getGroupId());
            result.add(GroupAddMessageVo.toVo(groupUser, userAccount, group));
        });

        //其他的群要邀请
        List<GroupUser> inviteGroupUsers = groupUserRepository.findAllByMemberAndDataStatusAndIsAgreeAndGroupType(userId, 1, 0, GroupType.INVITE);
        List<String> inviteGroupIds = inviteGroupUsers.stream().map(GroupUser::getGroupId).collect(Collectors.toList());
        List<Group> inviteGroups = groupRepository.findAllByIdInAndDataStatus(inviteGroupIds, 1);
        Map<String, Group> inviteGroupMap = inviteGroups.stream().collect(Collectors.toMap(Group::getId, group -> group, (k, v) -> v));
        List<String> inviteGroupMasterIds = inviteGroups.stream().map(Group::getGroupMaster).collect(Collectors.toList());
        Map<String, UserAccount> inviteUserAccountMap = userAccountRepository.findAllByIdInAndDataStatus(inviteGroupMasterIds, 1).stream().collect(Collectors.toMap(UserAccount::getId, userAccount -> userAccount, (old, now) -> now));
        inviteGroupUsers.forEach(groupUser -> {
            Group group = inviteGroupMap.get(groupUser.getGroupId());
            UserAccount userAccount = inviteUserAccountMap.get(group.getGroupMaster());
            result.add(GroupAddMessageVo.toVo(groupUser, userAccount, group));
        });

        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void intoGroup(String userId, String groupNumber) {
        groupRepository.findByGroupNumberAndDataStatus(groupNumber,1)
                .filter(group -> !group.getGroupMaster().equals(userId))
                .map(group -> {
                    if(groupUserRepository.findTopByGroupIdAndMemberAndDataStatus(group.getId(), userId, 1).isPresent()){
                        throw ExceptionPropertiesUtil.getException(ExceptionConstant.INTO_GROUP_ERROR);
                    }
                    GroupUser groupUser = new GroupUser();
                    groupUser.setGroupId(group.getId());
                    groupUser.setMember(userId);
                    groupUser.setGroupType(GroupType.APPLICATION);
                    groupUserRepository.save(groupUser);

                    UserAccount userAccount = userAccountRepository.findByIdAndDataStatus(userId, 1).orElseThrow();
                    UserAccount masterUserAccount = userAccountRepository.findByIdAndDataStatus(group.getGroupMaster(), 1).orElseThrow();
                    MessageDto messageDto = new MessageDto();
                    messageDto.setType(MessageType.REQUEST);
                    WebSocketServer.invite(messageDto ,userAccount, masterUserAccount);
                    return true;
                })
                .orElseThrow(() -> ExceptionPropertiesUtil.getException(ExceptionConstant.GROUP_ERROR));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addGroupUser(String userId, AddGroupUserRo addGroupUserRo) {
        groupRepository
                .findByIdAndDataStatus(addGroupUserRo.getGroupId(), 1)
                .filter(group -> group.getGroupMaster().equals(userId))
                .map(group -> {
                    List<String> existGroupUserIds = groupUserRepository.findAllByGroupIdAndDataStatus(group.getId(), 1).stream().map(GroupUser::getMember).collect(Collectors.toList());
                    //去掉关闭邀请的用户
                    List<String> addGroupUserIds = userAccountRepository.findAllByIdInAndDataStatus(addGroupUserRo.getUserIds(), 1)
                            .stream()
                            .filter(userAccount -> userAccount.getRefuseGroupAgree() == 0)
                            .map(UserAccount::getId)
                            .collect(Collectors.toList());
                    addGroupUserIds.removeAll(existGroupUserIds);
                    //需要邀请的用户
                    Map<String, UserAccount> addUserAccount = userAccountRepository.findAllByIdInAndDataStatus(addGroupUserIds, 1).stream().collect(Collectors.toMap(UserAccount::getId, userAccount -> userAccount, (old, now) -> now));
                    //自己
                    UserAccount userAccount = userAccountRepository.findByIdAndDataStatus(userId, 1).orElseThrow();

                    List<GroupUser> addGroupUsers = addGroupUserIds.stream().map(s -> {
                        GroupUser groupUser = new GroupUser();
                        //自动同意
                        if(addUserAccount.get(s).getAutoGroupAgree()==1){
                            groupUser.setIsAgree(1);
                        }
                        groupUser.setGroupId(group.getId());
                        groupUser.setMember(s);
                        groupUser.setGroupType(GroupType.INVITE);
                        //发送通知
                        MessageDto messageDto = new MessageDto();
                        messageDto.setType(MessageType.INVITE);
                        WebSocketServer.invite(messageDto ,userAccount, addUserAccount.get(s));
                        return groupUser;
                    }).collect(Collectors.toList());
                    groupUserRepository.saveAll(addGroupUsers);

                    return true;
                })
                .orElseThrow(() -> ExceptionPropertiesUtil.getException(ExceptionConstant.NO_PERMISSION));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createGroup(String userId, CreateGroupRo createGroupRo) {
        if(groupRepository.findByGroupNumberAndDataStatus(createGroupRo.getGroupNumber(),1).isPresent()){
            throw ExceptionPropertiesUtil.getException(ExceptionConstant.GROUP_EXIST);
        }
        Group group = new Group();
        group.setGroupNumber(createGroupRo.getGroupNumber());
        group.setGroupMaster(userId);
        group.setAvatar(createGroupRo.getAvatar());
        group.setName(createGroupRo.getName());

        Group group1 = groupRepository.saveAndFlush(group);

        GroupUser groupUser = new GroupUser();
        groupUser.setGroupType(GroupType.APPLICATION);
        groupUser.setMember(userId);
        groupUser.setIsAgree(1);
        groupUser.setGroupId(group1.getId());
        groupUserRepository.saveAndFlush(groupUser);

        return group1.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void agreeGroupUser(String userId, String id) {
        groupUserRepository.findByIdAndIsAgreeAndDataStatus(id, 0, 1).map(groupUser -> {
            //验证
            if(groupUser.getGroupType().equals(GroupType.INVITE)) {
                if (!userId.equals(groupUser.getMember())) {
                    throw ExceptionPropertiesUtil.getException(ExceptionConstant.NO_PERMISSION);
                }
                //通过
                groupUser.setIsAgree(1);
                return true;
            }else{
               return groupRepository.findByIdAndDataStatus(groupUser.getGroupId(), 1)
                        .map(group -> {
                            if(!group.getGroupMaster().equals(userId)){
                                throw ExceptionPropertiesUtil.getException(ExceptionConstant.NO_PERMISSION);
                            }
                            groupUser.setIsAgree(1);
                            return true;
                        })
                        .orElseThrow(()->ExceptionPropertiesUtil.getException(ExceptionConstant.GROUP_NULL));
            }
        }).orElseThrow(() -> ExceptionPropertiesUtil.getException(ExceptionConstant.GROUP_USER_NULL));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refusalFriend(String userId, String id) {
        groupUserRepository.findByIdAndIsAgreeAndDataStatus(id, 0, 1).map(groupUser -> {
            //验证
            if(groupUser.getGroupType().equals(GroupType.INVITE)) {
                if (!userId.equals(groupUser.getMember())) {
                    throw ExceptionPropertiesUtil.getException(ExceptionConstant.NO_PERMISSION);
                }
                //通过
                groupUser.setDataStatus(0);
                return true;
            }else{
                return groupRepository.findByIdAndDataStatus(groupUser.getGroupId(), 1)
                        .map(group -> {
                            if(!group.getGroupMaster().equals(userId)){
                                throw ExceptionPropertiesUtil.getException(ExceptionConstant.NO_PERMISSION);
                            }
                            groupUser.setDataStatus(0);
                            return true;
                        })
                        .orElseThrow(()->ExceptionPropertiesUtil.getException(ExceptionConstant.GROUP_NULL));
            }
        }).orElseThrow(() -> ExceptionPropertiesUtil.getException(ExceptionConstant.GROUP_USER_NULL));
    }

    @Override
    public GroupVo getGroupInfo(String groupId) {
        return groupRepository.findByIdAndDataStatus(groupId, 1)
                .map(GroupVo::toVo)
                .orElseThrow(()->ExceptionPropertiesUtil.getException(ExceptionConstant.GROUP_NULL));
    }

    @Override
    public List<MemberVo> getGroupMember(String groupId) {
        List<MemberVo> memberVos = new ArrayList<>();
        Group group = groupRepository.findByIdAndDataStatus(groupId, 1).orElseThrow(() -> ExceptionPropertiesUtil.getException(ExceptionConstant.GROUP_NULL));
        //添加群主
        userAccountRepository.findByIdAndDataStatus(group.getGroupMaster(), 1)
                .map(userAccount -> {
                    MemberVo memberVo = MemberVo.toVo(userAccount);
                    memberVos.add(memberVo);
                    return true;
                })
                .orElseThrow(()->ExceptionPropertiesUtil.getException(ExceptionConstant.USER_ACCOUNT_NULL));

        //添加群成员
        List<String> userIds = groupUserRepository.findAllByGroupIdAndDataStatusAndIsAgree(groupId, 1, 1).stream()
                .map(GroupUser::getMember)
                .filter(member -> !member.equals(group.getGroupMaster()))
                .collect(Collectors.toList());

        List<MemberVo> collect = userAccountRepository.findAllByIdInAndDataStatus(userIds, 1).stream().map(MemberVo::toVo).collect(Collectors.toList());
        memberVos.addAll(collect);
        return memberVos;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void exitGroup(String userId, String groupId) {
        groupRepository.findByIdAndDataStatus(groupId, 1)
                .map(group -> {
                    if(group.getGroupMaster().equals(userId)){
                        group.setDataStatus(0);
                        List<String> memberIds = groupUserRepository.findAllByGroupIdAndDataStatusAndIsAgree(groupId, 1, 1).stream().map(GroupUser::getMember).collect(Collectors.toList());
                        sessionRepository.findAllByUserIdInAndSessionIdAndDataStatus(memberIds, groupId, 1).forEach(session -> {
                            session.setDataStatus(0);
                        });
                        return true;
                    }else {
                       return groupUserRepository.findTopByGroupIdAndMemberAndDataStatus(groupId, userId, 1).map(groupUser -> {
                           groupUser.setDataStatus(0);
                           sessionRepository.findTopByUserIdAndSessionIdAndDataStatus(userId, groupId, 1).ifPresent(session -> {
                               session.setDataStatus(0);
                           });
                           return true;
                       }).orElseThrow(() -> ExceptionPropertiesUtil.getException(ExceptionConstant.GROUP_USER_NULL));
                    }
                })
                .orElseThrow(()->ExceptionPropertiesUtil.getException(ExceptionConstant.GROUP_NULL));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateGroup(String userId, UpdateGroupRo updateGroupRo) {
        groupRepository.findByIdAndDataStatus(updateGroupRo.getGroupId(),1)
                .filter(group -> group.getGroupMaster().equals(userId))
                .map(group -> {
                    group.setGroupMaster(updateGroupRo.getGroupMaster());
                    group.setName(updateGroupRo.getName());
                    group.setAvatar(updateGroupRo.getAvatar());
                    return true;
                })
                .orElseThrow(()->ExceptionPropertiesUtil.getException(ExceptionConstant.NO_PERMISSION));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void kickOut(String userId, KickOutRo kickOutRo) {
        groupRepository.findByIdAndDataStatus(kickOutRo.getGroupId(),1)
                .filter(group -> group.getGroupMaster().equals(userId))
                //不可以踢出自己
                .filter(group -> !group.getGroupMaster().equals(kickOutRo.getMember()))
                .map(group ->
                        groupUserRepository.findTopByGroupIdAndMemberAndDataStatusAndIsAgree(kickOutRo.getGroupId(), kickOutRo.getMember(), 1, 1)
                        .map(groupUser -> {
                            groupUser.setDataStatus(0);
                            return true;
                        }).orElseThrow(()->ExceptionPropertiesUtil.getException(ExceptionConstant.GROUP_USER_NULL)))
                .orElseThrow(()->ExceptionPropertiesUtil.getException(ExceptionConstant.NO_PERMISSION));
    }
}
